/*
 * Copyright (c) 2018 Hitevision
 */

package tw.com.hitevision.whiteboard.android.render;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.DashPathEffect;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathEffect;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.Log;

import androidx.annotation.ColorInt;
import androidx.core.content.ContextCompat;
import tw.com.hitevision.whiteboard.android.Props;
import tw.com.hitevision.whiteboard.android.R;
import tw.com.hitevision.whiteboard.android.display.DisplayDpiHelper;

public class SelectionBox extends BaseItem {
    private static final String TAG = SelectionBox.class.getSimpleName();

    private static final float DASH_ON_INTERVAL = 10.0f;
    private static final float DASH_OFF_INTERVAL = 10.0f;
    private static final float extend = 5.0f;
    private static final float MIN_RANGE = 44f;
    private static final int BUTTON_PADDING = 5;

    private final Drawable rotateIcon;
    private final Drawable handleIcon;
    private final Drawable trimIcon;

    private final int rotateIconOffsetX;
    private final int handleIconHitBuffer;
    private int trimingCorner;

    private final PathEffect pathEffect;
    private final Path path;
    private final float strokeWidth;
    @ColorInt
    private final int strokeColor;

    private Bitmap preview;
    private boolean previewVisible;
    private boolean outlineVisible;
    private boolean updatePath;

    private RectF trimRectF;

    /** Deferred x-translation for path. */
    private float pathDx;
    /** Deferred y-translation for path. */
    private float pathDy;

    /** Helper matrix */
    private Matrix mat;

    /** Helper RectF */
    private RectF rectF;

    private Rect previewSrc;
    private Rect previewDst;
    private boolean updatePreview;

    private PointF rotatePoint;
    private float rotation;

    private PointF scalePoint;
    private float scaleX;
    private float scaleY;

    private float downScaleWidthLimit;
    private float downScaleHeightLimit;
    private boolean isStickyNoteSelected;
    private boolean isImageTrimming;
    private boolean areSideHandlesEnabled;
    private int selectionRenderMode;

    private final Drawable textScaleIcon;
    private boolean textScaleVisible;
    private final Drawable headDragIcon;
    private PointF headPosition;
    private final Drawable tailDragIcon;
    private PointF tailPosition;
    private boolean headTailVisible;
    private float firstDragPaddingX;
    private float firstDragPaddingY;

    private Context context;

    public SelectionBox(Context context) {
        path = new Path();
        pathEffect = new DashPathEffect(new float[] {DASH_ON_INTERVAL, DASH_OFF_INTERVAL}, 0);
        mat = new Matrix();
        rectF = new RectF();
        previewSrc = new Rect();
        previewDst = new Rect();
        rotatePoint = new PointF();
        scalePoint = new PointF();
        trimRectF = new RectF();
        headPosition = new PointF();
        tailPosition = new PointF();

        strokeWidth = context.getResources().getDimensionPixelSize(R.dimen.selection_box_thickness);
        strokeColor = ContextCompat.getColor(context, R.color.eraser_menu_tint);

        rotateIcon = ContextCompat.getDrawable(context, R.drawable.ic_select_rotate_normal_4k);
        handleIcon = ContextCompat.getDrawable(context, R.drawable.select_corner_handle);
        trimIcon = ContextCompat.getDrawable(context, R.drawable.corner_2);
        rotateIconOffsetX = context.getResources().getDimensionPixelOffset(R.dimen.selection_rotate_offset_x);
        handleIconHitBuffer = context.getResources().getDimensionPixelOffset(R.dimen.selection_box_handle_hit_buffer);
        isImageTrimming = false;

        textScaleIcon = ContextCompat.getDrawable(context, R.drawable.select_corner_handle);
        textScaleVisible = false;

        headDragIcon = ContextCompat.getDrawable(context, R.drawable.select_corner_handle);
        tailDragIcon = ContextCompat.getDrawable(context, R.drawable.select_corner_handle);
        headTailVisible = false;

        areSideHandlesEnabled = true;

        this.context = context;
    }


//    public void updateByDpi() {
//        float factor = DisplayDpiHelper.getInstance().getFake4kFactor();
//
//        rotateHandleOffset = context.getResources().getDimensionPixelSize(R.dimen.selection_rotate_handle_offset) * factor;
//        strokeWidth = context.getResources().getDimensionPixelSize(R.dimen.selection_box_thickness);
//        handleSize = (int) (context.getResources().getDimensionPixelSize(R.dimen.selection_handle_size) * factor);
//        rotateIconOffsetX = (int) (context.getResources().getDimensionPixelOffset(R.dimen.selection_rotate_offset_x) * factor);
//        handleIconHitBuffer = (int) (context.getResources().getDimensionPixelOffset(R.dimen.selection_box_handle_hit_buffer) * factor);
//
//        extend = context.getResources().getDimensionPixelSize(R.dimen.selection_box_extend) * factor;
//        minRange = MIN_RANGE * factor;
//
//        if (factor >= 2) {
//            trimIcon = trimIcon4k;
//        } else {
//            trimIcon = trimIconFullHD;
//        }
//
//    }

    @Override
    public void render(Canvas canvas, Paint paint, Context context) {
        canvas.save();

        final float rotate = getRotation();
        if (rotate != 0) {
            final PointF point = getRotatePoint();
            canvas.rotate(rotate, point.x, point.y);
        }

        final float scaleX = getScaleX();
        final float scaleY = getScaleY();
        if (scaleX != 0 || scaleY != 0) {
            final PointF point = getScalePoint();
            canvas.scale(scaleX, scaleY, point.x, point.y);
        }

        if (outlineVisible) {
            if (updatePath) {
                updatePath();
                updateControls();
            } else if (pathDx != 0 || pathDy != 0) {
                mat.setTranslate(pathDx, pathDy);
                path.transform(mat);

                pathDx = 0;
                pathDy = 0;

                updateControls();
            }

            // draw outline

            paint.setColor(strokeColor);
            paint.setPathEffect(pathEffect);
            paint.setStyle(Paint.Style.STROKE);
            paint.setStrokeCap(Paint.Cap.BUTT);
            paint.setStrokeJoin(Paint.Join.BEVEL);
            paint.setStrokeWidth(strokeWidth);

            canvas.drawPath(path, paint);

            paint.setPathEffect(null);

            if (isImageTrimming) {
                // draw trimming controls
                drawTrimHandles(canvas, trimRectF);
            } else {
                // draw controls
                rotateIcon.draw(canvas);
                drawHandles(canvas);

                if (textScaleVisible) {
                    textScaleIcon.draw(canvas);
                }

                if (headTailVisible) {
                    headDragIcon.draw(canvas);
                    tailDragIcon.draw(canvas);
                }
            }
        }

        if (previewVisible && preview != null) {
            if (updatePreview) {
                updatePreview();
            }

            paint.setAlpha(0x8f);
            canvas.drawBitmap(preview, previewSrc, previewDst, paint);
        }

        canvas.restore();
    }

    private void drawHandles(Canvas canvas) {
        final float left = getLeft();
        final float top = getTop();
        final float right = getRight();
        final float bottom = getBottom();
        final float halfWidth = handleIcon.getIntrinsicWidth() / 2.0f;
        final float halfHeight = handleIcon.getIntrinsicHeight() / 2.0f;
        final float horizontalMid = (right + left) / 2;
        final float verticalMid = (top + bottom) / 2;

        // top left
        handleIcon.setBounds((int) (left - halfWidth), (int) (top - halfHeight),
                (int) (left + halfWidth), (int) (top + halfHeight));
        handleIcon.draw(canvas);

        // top right
        handleIcon.setBounds((int) (right - halfWidth), (int) (top - halfHeight),
                (int) (right + halfWidth), (int) (top + halfHeight));
        handleIcon.draw(canvas);

        // bottom right
        handleIcon.setBounds((int) (right - halfWidth), (int) (bottom - halfHeight),
                (int) (right + halfWidth), (int) (bottom + halfHeight));
        handleIcon.draw(canvas);

        // bottom left
        handleIcon.setBounds((int) (left - halfWidth), (int) (bottom - halfHeight),
                (int) (left + halfWidth), (int) (bottom + halfHeight));
        handleIcon.draw(canvas);

        if (areSideHandlesEnabled) {
            // top center
            handleIcon.setBounds((int) (horizontalMid - halfWidth), (int) (top - halfHeight),
                    (int) (horizontalMid + halfWidth), (int) (top + halfHeight));
            handleIcon.draw(canvas);

            // right center
            handleIcon.setBounds((int) (right - halfWidth), (int) (verticalMid - halfHeight),
                    (int) (right + halfWidth), (int) (verticalMid + halfHeight));
            handleIcon.draw(canvas);

            // bottom center
            handleIcon.setBounds((int) (horizontalMid - halfWidth), (int) (bottom - halfHeight),
                    (int) (horizontalMid + halfWidth), (int) (bottom + halfHeight));
            handleIcon.draw(canvas);

            // left center
            handleIcon.setBounds((int) (left - halfWidth), (int) (verticalMid - halfHeight),
                    (int) (left + halfWidth), (int) (verticalMid + halfHeight));
            handleIcon.draw(canvas);
        }
    }

    private void drawTrimHandles(Canvas canvas, RectF trimRectF) {
        final float left = trimRectF.left + extend;
        final float top = trimRectF.top + extend;
        final float right = trimRectF.right - extend;
        final float bottom = trimRectF.bottom - extend;
        final float halfWidth = trimIcon.getIntrinsicWidth() / 2.0f;
        final float halfHeight = trimIcon.getIntrinsicHeight() / 2.0f;

        // top left
        trimIcon.setBounds((int) (left - halfWidth), (int) (top - halfHeight),
                (int) (left + halfWidth), (int) (top + halfWidth));
        trimIcon.draw(canvas);

        // top right
        trimIcon.setBounds((int) (right - halfWidth), (int) (top - halfHeight),
                (int) (right + halfWidth), (int) (top + halfWidth));
        canvas.rotate(90, right, top);
        trimIcon.draw(canvas);

        canvas.rotate(-90, right, top);
        // bottom right
        trimIcon.setBounds((int) (right - halfWidth), (int) (bottom - halfHeight),
                (int) (right + halfWidth), (int) (bottom + halfWidth));
        canvas.rotate(180, right, bottom);
        trimIcon.draw(canvas);
        canvas.rotate(-180, right, bottom);

        // bottom left
        trimIcon.setBounds((int) (left - halfWidth), (int) (bottom - halfHeight),
                (int) (left + halfWidth), (int) (bottom + halfWidth));
        canvas.rotate(-90, left, bottom);
        trimIcon.draw(canvas);
        canvas.rotate(90, left, bottom);
    }

    public void setTrimRectF(RectF rectF) {
        trimRectF.set(rectF);
    }

    public void setTrimRectF(float left, float top, float right, float bottom) {
        trimRectF.set(left, top, right, bottom);
    }

    public RectF getTrimRectF() {
        return trimRectF;
    }

    public void setPreview(Bitmap bitmap) {
        preview = bitmap;
    }

    public Bitmap getPreview() {
        return preview;
    }

    public void hide() {
        previewVisible = false;
        outlineVisible = false;
        headTailVisible = false;
        textScaleVisible = false;
        isStickyNoteSelected = false;
        isImageTrimming = false;
        areSideHandlesEnabled = false;
    }

    public boolean isVisible() {
        return previewVisible || outlineVisible;
    }

    public void setPreviewVisible(boolean visible) {
        previewVisible = visible;
    }

    public void setOutlineVisible(boolean visible) {
        outlineVisible = visible;
    }

    public void setRotatePoint(float x, float y) {
        rotatePoint.set(x, y);
    }

    public PointF getRotatePoint() {
        return rotatePoint;
    }

    public float getRotation() {
        return rotation;
    }

    public void setRotation(float degrees) {
        this.rotation = degrees;
    }

    public PointF getScalePoint() {
        return scalePoint;
    }

    public float getScaleX() {
        return scaleX;
    }

    public float getScaleY() {
        return scaleY;
    }

    public void setScale(float scaleX, float scaleY) {
        this.scaleX = scaleX;
        this.scaleY = scaleY;
    }

    public boolean getHeadTailVisible() {
        return headTailVisible;
    }

    /**
     * set rotate in the case of redirection
     */
    public void setScalePoint(int controlMode) {
        // init scale rate
        setScale(1, 1);
        switch (controlMode) {
            case Control.HANDLES_LEFT_BOTTOM: {
                scalePoint.set(x + width, y);
                break;
            }
            case Control.HANDLES_LEFT_TOP: {
                scalePoint.set(x + width, y + height);
                break;
            }
            case Control.HANDLES_RIGHT_BOTTOM: {
                scalePoint.set(x, y);
                break;
            }
            case Control.HANDLES_RIGHT_TOP: {
                scalePoint.set(x, y + height);
                break;
            }
            case Control.HANDLES_LEFT: {
                scalePoint.set(x + width, y + height / 2);
                break;
            }
            case Control.HANDLES_TOP: {
                scalePoint.set(x + width / 2, y + height);
                break;
            }
            case Control.HANDLES_RIGHT: {
                scalePoint.set(x, y + height / 2);
                break;
            }
            case Control.HANDLES_BOTTOM: {
                scalePoint.set(x + width / 2, y);
                break;
            }
            case Control.TAILREDIRECT: {
                scalePoint.set(headPosition);
                rotatePoint.set(headPosition);
                break;
            }
            case Control.HEADREDIRECT: {
                scalePoint.set(tailPosition);
                rotatePoint.set(tailPosition);
                break;
            }
            default: {
                break;
            }
        }
    }

    public void setScalePoint(float x, float y) {
        scalePoint.set(x, y);
    }

    @Override
    public void moveBy(float dx, float dy) {
        super.moveBy(dx, dy);

        pathDx += dx;
        pathDy += dy;

        rectF.set(getLeft(), getTop(), getRight(), getBottom());
        rectF.roundOut(previewDst);
    }

    @Override
    public void setBounds(float x, float y, float width, float height) {
        super.setBounds(x, y, width, height);

        updatePath = true;
        updatePreview = true;
    }

    public int hitControls(float x, float y) {
        if (rotateIcon.getBounds().contains((int) x, (int) y)) {
            return Control.ROTATE;
        } else if (headDragIcon.getBounds().contains((int) x, (int) y)) {
            return Control.HEADREDIRECT;
        } else if (tailDragIcon.getBounds().contains((int) x, (int) y)) {
            return Control.TAILREDIRECT;
        } else {
            return hitHandles(x, y);
        }
    }

    public void setTrimingCorner(int input) {
        trimingCorner = input;
    }

    public void setHeadTailVisible(boolean t) {
        headTailVisible = t;
    }

    public void updateHeadTailPosition(float hx, float hy, float tx, float ty) {
        float buttonW = textScaleIcon.getIntrinsicWidth() / 1.35f; // half width
        float buttonH = textScaleIcon.getIntrinsicHeight() / 1.35f; // half height
        headDragIcon.setBounds(Math.round(hx - buttonW), Math.round(hy - buttonH), Math.round(hx + buttonW), Math.round(hy + buttonH));
        tailDragIcon.setBounds(Math.round(tx - buttonW), Math.round(ty - buttonH), Math.round(tx + buttonW), Math.round(ty + buttonH));
        headPosition.set(hx, hy);
        tailPosition.set(tx, ty);
    }

    public void draggingTrimmingCorner(float x, float y) {
        switch (trimingCorner) {
            case Control.HANDLES_LEFT_BOTTOM: {
                if (x > trimRectF.right - MIN_RANGE) {
                    trimRectF.left = trimRectF.right - MIN_RANGE;
                } else if (x < getLeft()) {
                    trimRectF.left = getLeft();
                } else {
                    trimRectF.left = x;
                }
                if (y < trimRectF.top + MIN_RANGE) {
                    trimRectF.bottom = trimRectF.top + MIN_RANGE;
                } else if (y > getBottom()) {
                    trimRectF.bottom = getBottom();
                } else {
                    trimRectF.bottom = y;
                }
                break;
            }
            case Control.HANDLES_LEFT_TOP: {
                if (x > trimRectF.right - MIN_RANGE) {
                    trimRectF.left = trimRectF.right - MIN_RANGE;
                } else if (x < getLeft()) {
                    trimRectF.left = getLeft();
                } else {
                    trimRectF.left = x;
                }
                if (y > trimRectF.bottom - MIN_RANGE) {
                    trimRectF.top = trimRectF.bottom - MIN_RANGE;
                } else if (y < getTop()) {
                    trimRectF.top = getTop();
                } else {
                    trimRectF.top = y;
                }
                break;
            }
            case Control.HANDLES_RIGHT_BOTTOM: {
                if (x < trimRectF.left + MIN_RANGE) {
                    trimRectF.right = trimRectF.left + MIN_RANGE;
                } else if (x > getRight()) {
                    trimRectF.right = getRight();
                } else {
                    trimRectF.right = x;
                }
                if (y < trimRectF.top + MIN_RANGE) {
                    trimRectF.bottom = trimRectF.top + MIN_RANGE;
                } else if (y > getBottom()) {
                    trimRectF.bottom = getBottom();
                } else {
                    trimRectF.bottom = y;
                }
                break;
            }
            case Control.HANDLES_RIGHT_TOP: {
                if (x < trimRectF.left + MIN_RANGE) {
                    trimRectF.right = trimRectF.left + MIN_RANGE;
                } else if (x > getRight()) {
                    trimRectF.right = getRight();
                } else {
                    trimRectF.right = x;
                }
                if (y > trimRectF.bottom - MIN_RANGE) {
                    trimRectF.top = trimRectF.bottom - MIN_RANGE;
                } else if (y < getTop()) {
                    trimRectF.top = getTop();
                } else {
                    trimRectF.top = y;
                }
                break;
            }
        }
        trimRectF.set(trimRectF);
    }

    // kept for tiny photo?
    public void checkTrimmingRect() {

        if (trimRectF.left > trimRectF.right) {
            trimRectF.left = trimRectF.right;
        }
        if (trimRectF.top > trimRectF.bottom) {
            trimRectF.top = trimRectF.bottom;
        }

        if (trimRectF.left < getX() + extend + 5) {
            trimRectF.left = getX() + extend + 5;
        }
        if (trimRectF.top < getY() + extend + 5) {
            trimRectF.top = getY() + extend + 5;
        }
        if (trimRectF.right > getX() + getWidth() - 2 * extend) {
            trimRectF.right = getX() + getWidth() - 2 * extend;
        }
        if (trimRectF.bottom > getY() + getHeight() - 2 * extend) {
            trimRectF.bottom = getY() + getHeight() - 2 * extend;
        }
        trimRectF.set(trimRectF);
    }

    public int hitControls(float[] textBounds, float x, float y, float totalDegree) {
        if (rotateIcon.getBounds().contains((int) x, (int) y)) {
            return Control.ROTATE;
        } else if (hitScaleButton(textBounds, x, y, totalDegree)) {
            return Control.HANDLES_TEXT_SCALE;
        } else {
            return hitHandles(x, y);
        }
    }

    private int hitHandles(float x, float y) {
        final float left = getLeft();
        final float top = getTop();
        final float right = getRight();
        final float bottom = getBottom();
        final float leftTrim = trimRectF.left;
        final float topTrim = trimRectF.top;
        final float rightTrim = trimRectF.right;
        final float bottomTrim = trimRectF.bottom;
        final float halfWidth = handleIcon.getIntrinsicWidth() / 2.0f + handleIconHitBuffer;
        final float halfHeight = handleIcon.getIntrinsicHeight() / 2.0f + handleIconHitBuffer;
        final float halfWidthTrim = trimIcon.getIntrinsicWidth() / 2.0f + handleIconHitBuffer;
        final float halfHeightTrim = trimIcon.getIntrinsicHeight() / 2.0f + handleIconHitBuffer;
        final float horizontalMid = (right + left) / 2;
        final float verticalMid = (top + bottom) / 2;

        if (isImageTrimming) {
            if (x >= leftTrim - halfWidthTrim && x <= leftTrim + halfWidthTrim && y >= topTrim - halfHeightTrim && y <= topTrim + halfWidthTrim) {
                return Control.TRIM_LEFT_TOP;
            } else if (x >= rightTrim - halfWidthTrim && x <= rightTrim + halfWidthTrim && y >= topTrim - halfHeightTrim && y <= topTrim + halfWidthTrim) {
                return Control.TRIM_RIGHT_TOP;
            } else if (x >= rightTrim - halfWidthTrim && x <= rightTrim + halfWidthTrim && y >= bottomTrim - halfHeightTrim && y <= bottomTrim + halfWidthTrim) {
                return Control.TRIM_RIGHT_BOTTOM;
            } else if (x >= leftTrim - halfWidthTrim && x <= leftTrim + halfWidthTrim && y >= bottomTrim - halfHeightTrim && y <= bottomTrim + halfWidthTrim) {
                return Control.TRIM_LEFT_BOTTOM;
            } else {
                return Control.NONE;
            }
        } else {
            if (x >= left - halfWidth && x <= left + halfWidth && y >= top - halfHeight && y <= top + halfWidth) {
                return Control.HANDLES_LEFT_TOP;
            } else if (x >= right - halfWidth && x <= right + halfWidth && y >= top - halfHeight && y <= top + halfWidth) {
                return Control.HANDLES_RIGHT_TOP;
            } else if (x >= right - halfWidth && x <= right + halfWidth && y >= bottom - halfHeight && y <= bottom + halfWidth) {
                return Control.HANDLES_RIGHT_BOTTOM;
            } else if (x >= left - halfWidth && x <= left + halfWidth && y >= bottom - halfHeight && y <= bottom + halfWidth) {
                return Control.HANDLES_LEFT_BOTTOM;
            }

            if (areSideHandlesEnabled) {
                if (x >= left - halfWidth && x <= left + halfWidth && y >= verticalMid - halfHeight && y <= verticalMid + halfWidth) {
                    return Control.HANDLES_LEFT;
                } else if (x >= horizontalMid - halfWidth && x <= horizontalMid + halfWidth && y >= top - halfHeight && y <= top + halfWidth) {
                    return Control.HANDLES_TOP;
                } else if (x >= right - halfWidth && x <= right + halfWidth && y >= verticalMid - halfHeight && y <= verticalMid + halfWidth) {
                    return Control.HANDLES_RIGHT;
                } else if (x >= horizontalMid - halfWidth && x <= horizontalMid + halfWidth && y >= bottom - halfHeight && y <= bottom + halfWidth) {
                    return Control.HANDLES_BOTTOM;
                }
            }

            return Control.NONE;
        }
    }

    private void updatePath() {
        updatePath = false;
        pathDx = 0;
        pathDy = 0;

        final float x = getX();
        final float y = getY();
        final float width = getWidth();
        final float height = getHeight();

        path.rewind();
        path.moveTo(x, y);
        path.lineTo(x, y + height);
        path.lineTo(x + width, y + height);
        path.lineTo(x + width, y);
        path.lineTo(x, y);

    }

    private void updateControls() {
        final float left = getLeft();
        final float top = getTop();
        final float right = getRight();
        final float bottom = getBottom();

        rotateIcon.setBounds((int) right + rotateIconOffsetX, (int) bottom,
                (int) right + rotateIcon.getIntrinsicWidth() + rotateIconOffsetX,
                (int) bottom + rotateIcon.getIntrinsicHeight());

    }

    private void updatePreview() {
        updatePreview = false;

        rectF.set(0, 0, getWidth(), getHeight());
        rectF.roundOut(previewSrc);

        rectF.set(getLeft(), getTop(), getRight(), getBottom());
        rectF.roundOut(previewDst);
    }

    @Override
    public void scaleFromPoint(float rateX, float rateY, PointF p) {
        // do nothing
    }

    @Override
    public void rotateFromPoint(float degree, PointF p) {
        // do nothing
    }

    @Override
    public BaseItem copy() {
        return null;
    }

    public static class Control {
        public static final int NONE = 0;
        public static final int ROTATE = 1;
        public static final int HANDLES_LEFT_TOP = 2;
        public static final int HANDLES_LEFT_BOTTOM = 3;
        public static final int HANDLES_RIGHT_BOTTOM = 4;
        public static final int HANDLES_RIGHT_TOP = 5;
        public static final int TRIM_LEFT_TOP = 6;
        public static final int TRIM_LEFT_BOTTOM = 7;
        public static final int TRIM_RIGHT_BOTTOM = 8;
        public static final int TRIM_RIGHT_TOP = 9;
        public static final int HANDLES_TEXT_SCALE = 10;
        public static final int HANDLES_LEFT = 11;
        public static final int HANDLES_TOP = 12;
        public static final int HANDLES_RIGHT = 13;
        public static final int HANDLES_BOTTOM = 14;
        public static final int HEADREDIRECT = 15;
        public static final int TAILREDIRECT = 16;
    }

    public static class ButtonMode {
        public static final int NORMAL = 0;
        public static final int STICKYNOTE = 1;
        public static final int TRIMMING = 2;

    }

    public boolean isStickyNoteSelected() {
        return isStickyNoteSelected;
    }

    public void setStickyNoteSelected(boolean selected) {
        isStickyNoteSelected = selected;
    }

    public void setTextScaleVisible(boolean b) {
        textScaleVisible = b;
    }

    public void updateScaleButtonBound(float[] bounds, float totalDegree) {
        float left = bounds[0];
        float top = bounds[1];
        float right = bounds[2];
        float bottom = bounds[3];

        float buttonW = textScaleIcon.getIntrinsicWidth() / 2.0f; // half width
        float buttonH = textScaleIcon.getIntrinsicHeight() / 2.0f; // half height
        float buttonCenterX = right - buttonW / 2.0f - BUTTON_PADDING;
        float buttonCenterY = (top + bottom) / 2.0f;

        if (totalDegree % 360.0f != 0.0f) {
            float centerX = getX() + getWidth() / 2.0f;
            float centerY = getY() + getHeight() / 2.0f;
            mat.reset();
            mat.setRotate(totalDegree, centerX, centerY);
            float[] pos = {buttonCenterX, buttonCenterY};
            mat.mapPoints(pos);
            buttonCenterX = pos[0];
            buttonCenterY = pos[1];
        }

        textScaleIcon.setBounds((int) (buttonCenterX - buttonW), (int) (buttonCenterY - buttonH), (int) (buttonCenterX + buttonW), (int) (buttonCenterY + buttonH));

    }

    private boolean hitScaleButton(float[] bounds, float x, float y, float totalDegree) {

        float textBoxRight = bounds[2];
        float textBoxBottom = bounds[3];

        if (totalDegree % 360.0f != 0.0f) {
            float centerX = getX() + getWidth() / 2.0f;
            float centerY = getY() + getHeight() / 2.0f;
            float[] pos = {x, y};
            mat.reset();
            mat.setRotate(-totalDegree, centerX, centerY);
            mat.mapPoints(pos);
            firstDragPaddingX = Math.abs(pos[0] - textBoxRight);
            firstDragPaddingY = Math.abs(pos[1] - textBoxBottom);
        } else {
            firstDragPaddingX = Math.abs(x - textBoxRight);
            firstDragPaddingY = Math.abs(y - textBoxBottom);
        }
        float buttonCenterX = textScaleIcon.getBounds().centerX();
        float buttonCenterY = textScaleIcon.getBounds().centerY();

        float buttonHitWidth = textScaleIcon.getBounds().width();
        float buttonHitHeight = textScaleIcon.getBounds().height();

        float left = buttonCenterX - buttonHitWidth;
        float right = buttonCenterX + buttonHitWidth;
        float top = buttonCenterY - buttonHitHeight;
        float bottom = buttonCenterY + buttonHitHeight;

        return (x >= left && x <= right && y >= top && y <= bottom);
    }

    public float[] getFirstDragPadding() {
        return new float[] {firstDragPaddingX, firstDragPaddingY};
    }

    public boolean isImageTrimming() {
        return isImageTrimming;
    }

    public void setImageTrimming(boolean trimming) {
        isImageTrimming = trimming;
    }

    public void setShowSideHandles(boolean enabled) {
        areSideHandlesEnabled = enabled;
    }

    public int getSelectionRenderMode() {
        return selectionRenderMode;
    }

    public void setSelectionRenderMode(int mode) {
        selectionRenderMode = mode;
    }

    public void setDownScaleLimit(float width, float height) {
        float wlimit = 34 / (width);
        float hlimit = 34 / (height);
        if (wlimit > downScaleWidthLimit) {
            downScaleWidthLimit = wlimit;
        }
        if (hlimit > downScaleHeightLimit) {
            downScaleHeightLimit = hlimit;
        }
        if (wlimit < 0 || width < 34) {
            downScaleWidthLimit = 1;
        }
        if (hlimit < 0 || height < 34) {
            downScaleHeightLimit = 1;
        }
    }

    public void resetScaleLimit() {
        downScaleHeightLimit = 0;
        downScaleWidthLimit = 0;
    }

    public float getDownScaleWidthLimit() {
        if (downScaleWidthLimit < 1) {
            return downScaleWidthLimit;
        } else {
            return 1;
        }
    }

    public float getDownScaleHeightLimit() {
        if (downScaleHeightLimit < 1) {
            return downScaleHeightLimit;
        } else {
            return 1;
        }
    }

    private static void log(String msg) {
        Log.d(TAG, msg);
    }
}
